#ifndef __M_SESSION_H
#define __M_SESSION_H
#include <stdint.h>
#include <memory>
#include <unordered_map>
#include "online.hpp"

typedef enum
{
    LOGIN,
    UNLOGIN
} ss_status;
class session
{
private:
    uint64_t _uid;                         // 会话对应用户ID
    uint64_t _ssid;                        // 会话ID
    ss_status _status;                     // 会话状态
    websocket_server::timer_ptr _timerPtr; // 定时器

public: // 默认函数
    session(uint64_t ssid, ss_status status = LOGIN) : _ssid(ssid), _status(status)
    {
        DLOG("会话id:%lu,地址：%p,被创建", _ssid, this);
    }
    ~session()
    {
        DLOG("会话id:%lu,地址：%p,被销毁", _ssid, this);
    }

public:
    // 初始化
    // 设置会话对应用户ID
    void set_user(uint64_t uid)
    {
        _uid = uid;
    }
    // 设置状态
    void set_status(bool ifSattus)
    {
        _status = ifSattus ? LOGIN : UNLOGIN;
    }
    // 设置定时器
    void set_timer(const websocket_server::timer_ptr &timerPtr)
    {
        _timerPtr = timerPtr;
    }

    // 获取会话信息
    // 获取会话id
    uint64_t get_ssid()
    {
        return _ssid;
    }
    // 获取定时器
    websocket_server::timer_ptr get_timer()
    {
        return _timerPtr;
    }
    // 获取会话用户ID
    uint64_t get_user_id()
    {
        return _uid;
    }
    // 会话是否在线
    bool is_login()
    {
        return _status == LOGIN;
    }
};

using session_ptr = std::shared_ptr<session>;
#define SESSION_TIMEOUT 30000 // 30S
#define SESSION_FOREVER -1
class session_manager
{
private:
    uint64_t _next_sid; // 下个会话id
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _sessions; // hash<会话ID，会话指针>
    websocket_server *_server;                           // 该会话模块，会使用到websock，进行sc的交流(websocket句柄)

public: // 默认函数
    session_manager(websocket_server *server, uint64_t start_sid = 1) : _server(server), _next_sid(start_sid)
    {
        DLOG("会话模块初始化完成");
    }
    ~session_manager()
    {
        DLOG("会话模块销毁成功");
    }

public: // session管理
    // 创建会话
    session_ptr create_session(uint64_t uid, ss_status status)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr newSessionPtr(new session(_next_sid, status));
        newSessionPtr->set_status(status);
        newSessionPtr->set_user(uid);
        _sessions.insert(make_pair(_next_sid++, newSessionPtr));
        return newSessionPtr;
    }

    // 获取指定会话
    session_ptr get_session_by_ssid(uint64_t sid)
    {
        auto it = _sessions.find(sid);
        if (it == _sessions.end())
            return session_ptr();
        return it->second;
    }

    // 追加一个重复的会话(用于set_session_expire_time函数)
    void append_session(session_ptr sessionPtr)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.insert(make_pair(sessionPtr->get_ssid(), sessionPtr));
    }

    // 设定会话生命周期
    bool set_session_expire_time(uint64_t ssid, int ms)
    {
        // 该会话是否存在
        session_ptr sessionPtr = get_session_by_ssid(ssid);
        if (sessionPtr.get() == nullptr)
        {
            DLOG("session ID:%lu not exists,set_session_expire_time failed", ssid);
            return false;
        }
        websocket_server::timer_ptr timerPtr = sessionPtr->get_timer();

        // 注：若会话内部没有定时器 -> 会话永久存在
        if (timerPtr.get() == nullptr && ms == SESSION_FOREVER)
        {
            // 1、原会话永久存在，设定永久存在 -> 啥也不干
            DLOG("将会话:%lu,从永久设置为永久:%d",ssid,ms);
            return true;
        }
        else if (timerPtr.get() != nullptr && ms == SESSION_FOREVER)
        {
            DLOG("将会话:%lu,从定时设置为永久：%d",ssid,ms);
            // 2、原会话定时删除，设定永久存在 -> 定时器替换为空定时器（注：这里有坑）
            timerPtr->cancel();
            // 坑一：取消不是直接没有，取消是直接执行对应callback
            // 坑二：定时器的取消，不是立即执行，可能还未取消就代码向下继续进行
            // 坑三：坑二看似可以归结于线程安全的问题，但是并不能直接加锁解决，
            // 其原始是：任务添加到了自己的任务队列里面 自己到时间从任务队列取任务执行，
            // 不受当前代码时序所控制的 “锁不住”
            // 解决方法：
            // 1、将删除的会话指针重新填回_sessions管理表
            // 2、填回去时，也使用定时器来执行（走任务队列 -> 由于是后来的任务，填回去的过程一定是在，取消完成后）
            sessionPtr->set_timer(websocket_server::timer_ptr());
            _server->set_timer(0, std::bind(&session_manager::append_session, this, sessionPtr));
            
        }
        else if (timerPtr.get() == nullptr && ms != SESSION_FOREVER)
        {
            DLOG("将会话:%lu永久,从永久设置为定时：%d",ssid,ms);
            // 3、原会话永久存在，设定定时删除 ->设定定时器
            websocket_server::timer_ptr newTimerPtr = _server->set_timer(ms,
                                                                         std::bind(&session_manager::remove_session, this, ssid));
            sessionPtr->set_timer(newTimerPtr);
        }
        else if (timerPtr.get() != nullptr && ms != SESSION_FOREVER)
        {
            ////3、原会话定时删除，设定定时删除（更新定时器） -> 替换定时器
            DLOG("将会话:%lu,从定时设置为定时：%d",ssid,ms);
            // 1、取消原定时器
            timerPtr->cancel();
            sessionPtr->set_timer(websocket_server::timer_ptr()); // 需要置空，再重新设置新的定时器(好习惯：这里已经cancle，直接覆盖也没问题)
            // 2、重新添加由于取消导致的_sessions移除
            _server->set_timer(0, std::bind(&session_manager::append_session, this, sessionPtr));
            // 3、添加新定时器
            websocket_server::timer_ptr newTimerPtr = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssid));
            sessionPtr->set_timer(newTimerPtr);
        }
        // DLOG("5");
        return true;
    }

    // 销毁会话
    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.erase(ssid);
    }
};
#endif